package com.deimos;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.apache.axiom.om.OMElement;
import org.apache.axis2.util.XMLUtils;
import org.vast.ows.OWSException;
import org.vast.ows.sps.FeasibilityReport;
import org.vast.ows.sps.GetFeasibilityResponse;
import org.vast.ows.sps.ReservationReport;
import org.vast.ows.sps.SPSException;
import org.vast.ows.sps.SPSUtils;
import org.vast.ows.sps.StatusReport;
import org.vast.ows.sps.TaskingRequest;
import org.vast.ows.sps.TaskingResponse;
import org.vast.ows.sps.StatusReport.TaskStatus;
import org.vast.util.DateTime;
import org.vast.xml.DOMHelper;
import org.vast.xml.DOMHelperException;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import com.deimos.dataBase.Event;
import com.deimos.dataBase.Sensor;
import com.deimos.sps.GetFeasibility.Operation;
import com.spotimage.eosps.EOReportHelper;
import com.spotimage.eosps.EOSPSUtils;
import com.spotimage.eosps.EOConstants.ReportType;

public class UtilsDeimos {

    public static String DATE_FORMAT_CFI = "yyyy-MM-dd'_'HH:mm:ss.SSSSSS";
    public static String DATE_FORMAT_HMA = "yyyy-MM-dd'T'HH:mm:ss'Z'";

    public static SimpleDateFormat sdf_cfi = new SimpleDateFormat(DATE_FORMAT_CFI);
    public static SimpleDateFormat sdf_hma = new SimpleDateFormat(DATE_FORMAT_HMA);

    public static Schema schema = null;

    @SuppressWarnings("unchecked")
    public static StatusReport generateReport(Event event) {
	ReservationReport rs = null;
	rs = new ReservationReport();
	rs.setRequestStatus(event.getRequestStatus());
	rs.setTaskStatus(event.getTaskStatus());
	rs.setTaskID(event.getTask().getTaskId());
	rs.setDescription(event.getDescription());

	try {
	    Iterator it = readParams(event.getResponseParameters()).getReport().getExtensions().entrySet().iterator();
	    while (it.hasNext()) {
		Map.Entry e = (Map.Entry) it.next();
		rs.addExtension((Element) e.getValue());
	    }
	} catch (Exception e) {
	}

	if (event.getTask().getDelay() != null)
	    rs.setEstimatedDelay(event.getTask().getDelay().intValue());
	if (event.getEstimatedToC() != null)
	    rs.setEstimatedToC(new DateTime(event.getEstimatedToC().getTime()));
	if (event.getUpdateDate() != null)
	    rs.setLastUpdate(new DateTime(event.getUpdateDate().getTime()));
	rs.setTitle(event.getTask().getTitle());
	rs.setSensorID(event.getTask().getSensorId());

	if (event.getTaskStatus() == TaskStatus.Reserved) {
	    if (event.getExpirationDate() != null)
		rs.setReservationExpiration(new DateTime(event.getExpirationDate().getTime()));
	} else {
	    return getStatusReport(rs);
	}

	return rs;
    }

    @SuppressWarnings("unchecked")
    public static TaskingResponse readParams(String params) throws DOMHelperException, OWSException {
	if (params == null)
	    return null;
	SPSUtils utils = new SPSUtils();
	InputStream is = new ByteArrayInputStream(params.getBytes());
	DOMHelper domResponse = new DOMHelper(is, false);
	TaskingResponse response = utils.readTaskingResponse(domResponse, domResponse.getRootElement(), "2.0");

	EOReportHelper eohelper = new EOReportHelper(response.getReport());
	if (eohelper.getReportType() != null) {
	    eohelper.writeReportInfo();
	    response.getReport().touch();
	}

	return response;
    }

    public static TaskingRequest readRequest(String params) throws DOMHelperException, OWSException {
	if (params == null)
	    return null;
	SPSUtils utils = new SPSUtils();
	InputStream is = new ByteArrayInputStream(params.getBytes());
	DOMHelper domRequest = new DOMHelper(is, false);
	TaskingRequest request = (TaskingRequest) utils.readXMLQuery(domRequest, domRequest.getRootElement());
	return request;
    }

    @SuppressWarnings("unchecked")
    public static String getParameters(TaskingResponse response) {
	String parameters = null;
	OMElement el = null;
	try {
	    SPSUtils utils = new SPSUtils();
	    DOMHelper domResponse = new DOMHelper();
	    Element eltResponse = utils.buildXMLResponse(domResponse, response);
	    el = XMLUtils.toOM(eltResponse);
	    parameters = el.toString();
	} catch (Exception ex) {
	    try {
		EOSPSUtils utils = new EOSPSUtils();
		DOMHelper domResponse = new DOMHelper();
		Element eltResponse = utils.buildXMLResponse(domResponse, response);
		el = XMLUtils.toOM(eltResponse);
		parameters = el.toString();
	    } catch (Exception e) {
		ex.printStackTrace();
	    }
	    ex.printStackTrace();
	}
	return parameters;
    }

    public static StatusReport getStatusReport(ReservationReport rs) {
	StatusReport sr = new StatusReport();
	sr.setDescription(rs.getDescription());
	if (rs.getEstimatedToC() != null)
	    sr.setEstimatedDelay((int) rs.getEstimatedDelay());
	sr.setEstimatedToC(rs.getEstimatedToC());
	sr.getExtensions().putAll(rs.getExtensions());
	/*
	 * Iterator it = rs.getExtensions().entrySet().iterator(); while
	 * (it.hasNext()) { Map.Entry e = (Map.Entry) it.next();
	 * sr.addExtension((Element) e.getValue()); }
	 */
	sr.setLastUpdate(rs.getLastUpdate());
	sr.setSensorID(rs.getSensorID());
	sr.setRequestStatus(rs.getRequestStatus());
	sr.setTaskStatus(rs.getTaskStatus());
	sr.setTaskID(rs.getTaskID());
	sr.setTitle(rs.getTitle());
	return sr;
    }
    
    public static ReservationReport getReservationReport(StatusReport sr) {
	ReservationReport rs = new ReservationReport();
	rs.setDescription(sr.getDescription());
	if (rs.getEstimatedToC() != null)
	    rs.setEstimatedDelay((int) sr.getEstimatedDelay());
	rs.setEstimatedToC(sr.getEstimatedToC());
	rs.getExtensions().putAll(sr.getExtensions());
	/*
	 * Iterator it = rs.getExtensions().entrySet().iterator(); while
	 * (it.hasNext()) { Map.Entry e = (Map.Entry) it.next();
	 * sr.addExtension((Element) e.getValue()); }
	 */
	rs.setLastUpdate(sr.getLastUpdate());
	rs.setSensorID(sr.getSensorID());
	rs.setRequestStatus(sr.getRequestStatus());
	rs.setTaskStatus(sr.getTaskStatus());
	rs.setTaskID(sr.getTaskID());
	rs.setTitle(sr.getTitle());
	return rs;
    }

    public static String getParameters(TaskingRequest request) {
	String parameters = null;
	OMElement el = null;
	try {
	    SPSUtils utils = new SPSUtils();
	    DOMHelper domRequest = new DOMHelper();
	    request.getExtensions().clear();
	    Element eltRequest = utils.buildXMLQuery(domRequest, request);
	    el = XMLUtils.toOM(eltRequest);
	    parameters = el.toString();
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
	return parameters;
    }

    @SuppressWarnings("unchecked")
    public static boolean validateExtension(OMElement request) throws Exception {
	try {
	    OMElement parent = request.getFirstElement();
	    Iterator iter = parent.getChildElements();
	    if (parent.getLocalName() == "extension") {
		int i = 0, j = 0;
		while (iter.hasNext()) {
		    OMElement aux = (OMElement) iter.next();
		    if (aux == null)
			return true;
		    if (aux.getLocalName() != "FeasibilityLevel" && aux.getLocalName() != "ReferenceFeasibilityID") {
			throw new OWSException(OWSException.invalid_param_code, "extension");
		    } else if (aux.getLocalName() == "FeasibilityLevel" && aux.getText().compareTo("SIMPLE") != 0 && aux.getText().compareTo("COMPLETE") != 0 && !aux.getText().matches("other: \\w{2,}")) {
			throw new OWSException(OWSException.invalid_param_code, "FeasibilityLevel");
		    } else if (aux.getLocalName() == "FeasibilityLevel" && aux.getFirstElement() != null) {
			throw new OWSException(OWSException.invalid_param_code, aux.getFirstElement().getLocalName());
		    } else if (aux.getLocalName() == "ReferenceFeasibilityID" && new URI(aux.getText()) == null) {
			throw new OWSException(OWSException.invalid_param_code, "ReferenceFeasibilityID");
		    } else if (aux.getLocalName() == "ReferenceFeasibilityID" && aux.getFirstElement() == null)
			throw new OWSException(OWSException.invalid_param_code, aux.getFirstElement().getLocalName());
		    if (aux.getLocalName() != "FeasibilityLevel")
			i++;
		    if (aux.getLocalName() != "ReferenceFeasibilityID")
			j++;
		    if (i > 1 || j > 1)
			throw new OWSException(OWSException.invalid_param_code, "extension");
		}
	    }
	} catch (Exception e) {
	    if (e instanceof URISyntaxException)
		throw new OWSException(OWSException.invalid_request_code, "ReferenceFeasibilityID");
	    if (e instanceof OWSException)
		throw e;
	    else
		throw new OWSException(OWSException.invalid_param_code, "extension");
	}
	return true;
    }

    public static class XmlDefaultHandler extends DefaultHandler {
	/** @see org.xml.sax. ErrorHandler#error(SAXParseException) */
	public void error(SAXParseException spe) throws SAXException {
	    throw spe;
	}

	/** @see org.xml.sax. ErrorHandler#fatalError(SAXParseException) */
	public void fatalError(SAXParseException spe) throws SAXException {
	    throw spe;
	}
    }

    public static boolean validate(OMElement request) throws SPSException {
	Date now = new Date();
	final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";

	final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
	final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";

	File file = new File(UtilsDeimos.class.getResource("UtilsDeimos.class").getPath().toString());
	final String sps = (file.getParent() + "/../../XSD/sps/2.0/sps.xsd").replace("file:", "");
	final String swe = (file.getParent() + "/../../XSD/swe/2.0/swe.xsd").replace("file:", "");
	final String swes = (file.getParent() + "/../../XSD/sweCommon/2.0/swes.xsd").replace("file:", "");
	final String eop = (file.getParent() + "/../../XSD/eop/2.0/eop.xsd").replace("file:", "");
	final String eosps = (file.getParent() + "/../../XSD/eosps/2.0/eosps.xsd").replace("file:", "");
	final String[] schemas = { sps, swe, swes, eop, eosps };

	DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
	documentBuilderFactory.setNamespaceAware(true);
	documentBuilderFactory.setValidating(true);

	try {
	    documentBuilderFactory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
	    documentBuilderFactory.setAttribute(JAXP_SCHEMA_SOURCE, schemas);

	    DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
	    documentBuilder.setErrorHandler(new DefaultHandler());
	    // Document parse = documentBuilder.parse(new
	    // ByteArrayInputStream(request.toString().getBytes("UTF-8")));

	    SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

	    if (schema == null)
		schema = factory.newSchema(new File(sps));

	    Validator validator = schema.newValidator();
	    validator.validate(new DOMSource(XMLUtils.toDOM(request)));

	    System.out.println((new Date().getTime() - now.getTime()) / 1000. + " sg.");
	    return true;
	} catch (Exception e) {
	    String code = SPSException.invalid_param_code, locator;
	    if (e.getMessage().split("'").length > 1)
		locator = e.getMessage().split("'")[1];
	    else
		locator = "taskingParameters";
	    if (locator.split(":").length > 1)
		locator = locator.split(":")[1];
	    else if (e.getMessage().split("'").length > 4)
		locator = e.getMessage().split("'")[3].replace("Type", "");
	    String badValue = null;
	    String invalidXML = "Invalid XML Request";
	    throw new SPSException(code, locator, badValue, invalidXML + ": " + e.getMessage());
	}
    }

    @SuppressWarnings("unchecked")
    public static void feasibilityStudyToProgress(FeasibilityReport feasibilityReport, Operation operation, Sensor sensor) {
	try {
	    TaskingResponse response = new GetFeasibilityResponse();
	    EOReportHelper eohelper = new EOReportHelper(feasibilityReport);
	    feasibilityReport.getExtensions().clear();
	    response.setReport(new ReservationReport());
	    EOReportHelper neweohelper;
	    neweohelper = new EOReportHelper(response, ReportType.PROG_STATUS);
	    if (Operation.STUDIED == operation || !sensor.isSyn())
		neweohelper.setEstimatedCost(eohelper.getEstimatedCost());
	    if (Operation.STUDIED == operation || !sensor.isSyn())
		for (int j = 0; j < eohelper.getInformationUsed().size(); j++)
		    neweohelper.addInformationUsed(eohelper.getInformationUsed().get(j));
	    // neweohelper.setExpirationDelay(eohelper.ge());
	    if (Operation.STUDIED == operation || !sensor.isSyn())
		neweohelper.setOverallSuccessRate(eohelper.getOverallSuccessRate());
	    if (Operation.STUDIED == operation || !sensor.isSyn())
		neweohelper.setExpirationDate(eohelper.getExpirationDate());
	    for (int i = 0; i < eohelper.getNumSegments(); i++) {
		eohelper.loadSegment(i);
		neweohelper.addSegment();
		neweohelper.loadSegment(neweohelper.getNumSegments() - 1);
		neweohelper.setID(eohelper.getID());
		neweohelper.setObjectStatus(eohelper.getObjectStatus());
		neweohelper.setPlatformID(eohelper.getPlatformID());
		neweohelper.setPlatformName(eohelper.getPlatformName());
		neweohelper.setInstrumentName(eohelper.getInstrumentName());
		neweohelper.setInstrumentMode(eohelper.getInstrumentMode());
		if (Operation.STUDIED == operation || !sensor.isSyn())
		    neweohelper.setExpirationDate(eohelper.getExpirationDate());
		neweohelper.setAcquisitionStart(eohelper.getAcquisitionStart());
		neweohelper.setAcquisitionStop(eohelper.getAcquisitionStop());
		neweohelper.setFootprint(eohelper.getFootprint());
		neweohelper.setNumGridCells(eohelper.getNumGridCells());
	    }
	    neweohelper.writeReportInfo();
	    response.getReport().touch();
	    feasibilityReport.getExtensions().clear();
	    feasibilityReport.getExtensions().putAll(response.getReport().getExtensions());
	} catch (Exception e) {
	    e.printStackTrace();
	}
    }
}
